package net.cl.tools;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Map.Entry;
import java.util.zip.GZIPInputStream;

import net.cl.utils.IOUtil;

/**
 * HTTP请求代理类
 * 
 * @author caol64@gmail.com
 */
public class HttpProxy {

	private String url;
	private String method;
	private int connectTimeout = 0;
	private int readTimeout = 0;
	private int retries = 3;
	private HttpURLConnection conn;
	private String proxyHost;
	private String proxyPort;
	private String proxyUser;
	private String proxyPassword;
	private String userAgent;
	private String encoding;
	Properties prop = System.getProperties();
	private Map<String, String> requestHeader = new HashMap<String, String>();
	private Map<String, String> postParams;
	private static final String USER_AGENT = "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.2) Gecko/20100115 Firefox/3.6 (.NET CLR 3.5.30729)";
	public static final String METHOD_GET = "GET";
	public static final String METHOD_POST = "POST";
	public static final String METHOD_DELETE = "DELETE";
	public static final String METHOD_PUT = "PUT";
	public static final String METHOD_HEAD = "HEAD";

	public HttpProxy() {

	}

	public HttpProxy(String url) {
		this.url = url;
	}

	private int openConnection() throws IOException {
		if (retries < 1)
			retries = 1;
		URL u = new URL(url);
		conn = (HttpURLConnection) u.openConnection();
		conn.setRequestMethod(method == null ? METHOD_GET : method);
		conn.setConnectTimeout(connectTimeout);
		conn.setReadTimeout(readTimeout);
		if (proxyHost != null)
			prop.setProperty("http.proxyHost", proxyHost);
		if (proxyPort != null)
			prop.setProperty("http.proxyPort", proxyPort);
		if (proxyUser != null)
			prop.setProperty("http.proxyUser", proxyUser);
		if (proxyPassword != null)
			prop.setProperty("http.proxyPassword", proxyPassword);
		requestHeader.put("User-Agent", userAgent == null ? USER_AGENT : userAgent);
		for (Iterator<Entry<String, String>> iterator = requestHeader.entrySet().iterator(); iterator.hasNext();) {
			Entry<String, String> entry = iterator.next();
			conn.setRequestProperty(entry.getKey(), entry.getValue());
		}
		if (METHOD_POST.equals(method) && postParams != null && !postParams.isEmpty()) {
			StringBuilder params = new StringBuilder();
			for (Iterator<Entry<String, String>> iterator = postParams.entrySet().iterator(); iterator.hasNext();) {
				Entry<String, String> entry = iterator.next();
				params.append(entry.getKey());
				params.append("=");
				params.append(entry.getValue());
				params.append("&");
			}
			if (params.length() > 0)
				params.deleteCharAt(params.length() - 1);
			byte[] b = params.toString().getBytes();
			conn.getOutputStream().write(b, 0, b.length);
			conn.getOutputStream().flush();
			conn.getOutputStream().close();
		}
		return conn.getResponseCode();
	}

	private String getResponseString() throws IOException {
		String s = null;
		GZIPInputStream gzin = null;
		int times = 0;
		while (true) {
			times++;
			try {
				int status = openConnection();
				if (status != HttpURLConnection.HTTP_OK) {
					if (times == retries) {
						break;
					}
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
					}
				} else {
					InputStream is = conn.getInputStream();
					if (conn.getContentEncoding() != null
							&& conn.getContentEncoding().toLowerCase().indexOf("gzip") > -1) {
						gzin = new GZIPInputStream(is);
						s = IOUtil.toString(gzin, encoding);
					} else
						s = IOUtil.toString(is, encoding == null ? conn.getContentEncoding() : encoding);
					break;
				}
			} catch (IOException e) {
				if (times == retries) {
					throw e;
				}
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e1) {
				}
			} finally {
				if (gzin != null)
					gzin.close();
				if (conn != null)
					conn.disconnect();
			}
		}
		return s;
	}

	public long download(String targetPath) throws IOException {
		long l = 0;
		int times = 0;
		while (true) {
			times++;
			try {
				int status = openConnection();
				if (status != HttpURLConnection.HTTP_OK) {
					if (times == retries) {
						break;
					}
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
					}
				} else {
					InputStream is = conn.getInputStream();
					FileOutputStream fos = new FileOutputStream(targetPath);
					l = IOUtil.copyAndClose(is, fos);
					break;
				}
			} catch (IOException e) {
				if (times == retries) {
					throw e;
				}
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e1) {
				}
			} finally {
				if (conn != null)
					conn.disconnect();
			}
		}
		return l;
	}

	public String get() throws IOException {
		return getResponseString();
	}

	public String post() throws IOException {
		this.method = METHOD_POST;
		return getResponseString();
	}

	public String delete() throws IOException {
		this.method = METHOD_DELETE;
		return getResponseString();
	}

	public String put() throws IOException {
		this.method = METHOD_PUT;
		return getResponseString();
	}

	public Header head() throws IOException {
		URL u = new URL(url);
		conn = (HttpURLConnection) u.openConnection();
		conn.setInstanceFollowRedirects(false);
		conn.setRequestMethod(METHOD_HEAD);
		conn.setRequestProperty("Accept-Encoding", "gzip, deflate");
		conn.connect();
		Header header = new Header(conn.getHeaderFields());
		return header;
	}

	public String finalRedirect() throws IOException {
		int statusCode = 0;
		String url2 = url;
		do {
			try {
				URL u = new URL(url2);
				conn = (HttpURLConnection) u.openConnection();
				conn.setInstanceFollowRedirects(false);
				statusCode = conn.getResponseCode();
				if (statusCode == HttpURLConnection.HTTP_MOVED_PERM || statusCode == HttpURLConnection.HTTP_MOVED_TEMP) {
					url2 = conn.getHeaderField("Location");
				}
			} finally {
				if (conn != null)
					conn.disconnect();
			}
		} while (statusCode == HttpURLConnection.HTTP_MOVED_PERM || statusCode == HttpURLConnection.HTTP_MOVED_TEMP);
		return url2;
	}

	public HttpProxy url(String url) {
		this.url = url;
		return this;
	}

	public HttpProxy method(String method) {
		this.method = method.toUpperCase();
		return this;
	}

	public HttpProxy encoding(String encoding) {
		this.encoding = encoding;
		return this;
	}

	public HttpProxy setConnectTimeout(int connectTimeout) {
		this.connectTimeout = connectTimeout;
		return this;
	}

	public HttpProxy setReadTimeout(int readTimeout) {
		this.readTimeout = readTimeout;
		return this;
	}

	public HttpProxy setProxyPassword(String proxyPassword) {
		this.proxyPassword = proxyPassword;
		return this;
	}

	public HttpProxy setProxyHost(String proxyHost) {
		this.proxyHost = proxyHost;
		return this;
	}

	public HttpProxy setProxyPort(String proxyPort) {
		this.proxyPort = proxyPort;
		return this;
	}

	public HttpProxy setProxyUser(String proxyUser) {
		this.proxyUser = proxyUser;
		return this;
	}

	public void setPostParams(Map<String, String> postParams) {
		this.postParams = postParams;
	}

	public HttpProxy requestHeader(String key, String value) {
		requestHeader.put(key, value);
		return this;
	}

	public HttpProxy retry(int times) {
		this.retries = times;
		return this;
	}

	public HttpProxy userAgent(String userAgent) {
		this.userAgent = userAgent;
		return this;
	}

	class Header {
		private Map<String, List<String>> header;

		public Header(Map<String, List<String>> header) {
			this.header = header;
		}

		private String get(String key) {
			if (header.get(key) == null)
				return null;
			return header.get(key).get(0);
		}

		public int getResponseCode() {
			String statusLine = get(null);
			if (statusLine.startsWith("HTTP/1.")) {
				int codePos = statusLine.indexOf(' ');
				if (codePos > 0) {
					int phrasePos = statusLine.indexOf(' ', codePos + 1);
					if (phrasePos < 0)
						phrasePos = statusLine.length();
					try {
						return Integer.parseInt(statusLine.substring(codePos + 1, phrasePos));
					} catch (NumberFormatException e) {
					}
				}
			}
			return -1;
		}

		public String getServer() {
			return get("Server");
		}

		public long getContentLength() {
			String statusLine = get("Content-Length");
			try {
				return Long.parseLong(statusLine);
			} catch (NumberFormatException e) {
				return -1L;
			}
		}

		public String getContentType() {
			return get("Content-Type");
		}

		public String getEncoding() {
			String statusLine = get("Content-Encoding");
			if (statusLine != null)
				return statusLine;
			statusLine = getContentType();
			if (statusLine != null) {
				int pos = statusLine.indexOf("charset=");
				if (pos > -1) {
					statusLine = statusLine.substring(pos + 8);
					return statusLine;
				}
			}
			return null;
		}

		public String getRedirectLocation() {
			return get("Location");
		}
	}

	public static void main(String[] args) {
		HttpProxy p = new HttpProxy("http://localhost/wp/");
		try {
			System.out.println(p.head().getEncoding());
			// p.download("D:/a.html");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

}